<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport"
        content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
  <title>虚拟列表</title>
  <style>
  * {
    padding: 0;
    margin: 0;
  }

  [v-cloak] {
    visibility: hidden;
  }

  #app {
  }

  .banner {
    border-radius: 6px;
    margin: 10px;
    color: darkgray;
    height: 200px;
    line-height: 200px;
    text-align: center;
    font-size: 22px;
    background-color: lightgray;
  }

  #list {
    /*background-color: #f0f8ff;*/
  }

  .item {
    position: relative;
    height: 100px;
    display: flex;
  }

  .item:after {
    position: absolute;
    display: block;
    content: '';
    width: 100%;
    left: 0;
    bottom: 0;
    height: 1px;
    background-color: #b3b3b3;
  }

  .left {
    position: relative;
    flex: 1;
  }

  .left:before, .left:after {
    position: absolute;
    display: block;
    content: '';
    height: 12px;
    left: 10px;
    background-color: #dee2e6;
  }

  .left:before {
    top: 10px;
    width: 80%;
  }

  .left::after {
    top: 32px;
    width: 50%;
  }

  .right {
    display: flex;
    align-items: center;
    justify-content: center;
    width: 80px;
    height: 80px;
    margin: 10px;
    border-radius: 6px;
    background-color: #dee2e6;
    color: darkgray;
    font-size: 22px;
  }
  </style>
</head>
<body>
<div id="app">
  <div class="banner">Banner</div>
  <div class="banner">Banner</div>
  <div class="banner">Banner</div>

  <div id="list" ref="list">
    <div class="full" :style="fillStyle">
      <div class="list-box" v-cloak>
        <div class="item" v-for="(item,index) in showItems" :key="index">
          <div class="left"></div>
          <div class="right">{{item}}.jpg</div>
        </div>
      </div>
    </div>
  </div>

  <div class="banner">Banner</div>
  <div class="banner">Banner</div>
</div>
<script src="./lib/vue.min.js"></script>
<script>
function throttle(fn, wait) {
  let inThrottle, lastFn, lastTime
  return function () {
    const context = this,
      args = arguments
    if (!inThrottle) {
      fn.apply(context, args)
      lastTime = Date.now()
      inThrottle = true
    } else {
      clearTimeout(lastFn)
      lastFn = setTimeout(function () {
        if (Date.now() - lastTime >= wait) {
          fn.apply(context, args)
          lastTime = Date.now()
        }
      }, Math.max(wait - (Date.now() - lastTime), 0))
    }
  }
}

new Vue({
  el: '#app',
  data: {
    pages: 0,
    containSize: 0,
    startIndex: 0,
    oneItemHeight: 100,
    time: 0,
    listOffsetTop: 0,
    overflowStyle: { overflow: 'hidden' },
    loading: false,
    items: [],
  },
  computed: {
    fillStyle() {
      const bannerHeight = 0
      return {
        paddingTop: `${this.startIndex * this.oneItemHeight + bannerHeight}px`,
        paddingBottom: `${(this.items.length - this.endIndex) * 100 + bannerHeight}px`
      }
    },
    endIndex() {
      let endIndex = this.startIndex + this.containSize
      if (!this.items[endIndex]) {
        endIndex = this.items.length
      }
      return endIndex
    },
    showItems() {
      return this.items.slice(this.startIndex, this.endIndex)
    },
  },
  created() {
    this.genItems()
  },
  mounted() {
    this.$nextTick(() => {
      this.getContainSize()
      document.defaultView.addEventListener('scroll', throttle(this.handleScroll, 300), {
        passive: true,
        capture: false
      })
    })
  },
  methods: {
    genItems(num = 10) {
      this.pages++
      this.loading = true
      setTimeout(() => {
        const lists = []
        const len = this.items.length
        for (let i = len + 1; i <= len + num; i++) {
          lists.push(i)
        }
        this.items = this.items.concat(lists)
        this.loading = false
        // this.time = 500
      }, this.time)
    },
    getContainSize() {
      this.listOffsetTop = this.$refs.list.offsetTop
      this.containSize = ~~(window.innerHeight / this.oneItemHeight) + 2
    },
    handleScroll() {
      let scrollTop = (document.documentElement.scrollTop || document.body.scrollTop)
      scrollTop = scrollTop - this.listOffsetTop
      if (scrollTop < 0) {
        scrollTop = 0
      }
      this.startIndex = ~~(scrollTop / this.oneItemHeight)
      // console.log(this.startIndex)
      if (this.startIndex + this.containSize > this.items.length - 1 && !this.loading) {
        console.log('到底部了', this.pages)
        // 最多只能加载5页
        if (this.pages < 5) {
          this.genItems()
        }
      }
    }
  }
})
</script>
</body>
</html>
